using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System;
using System.Linq;
using System.IO;
using System.Windows.Input;
using System.ComponentModel;
using System.Text;
using System.Data;

namespace RazorSourceGenerator.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        private const string CONFIG_FILTER = "Config Files(*.config)|*.config|Xml Files(*.xml)|*.xml|All Files(*.*)|*.*";
        private const int RECENT_FILES_LIMIT = 5;
        private const string GLOBAL_PARAMS_FILE = "GlobalParameters.xml";

        private ConfigViewModel _configViewModel;
        private Microsoft.Win32.OpenFileDialog _openFileDialog;
        private Microsoft.Win32.SaveFileDialog _saveFileDialog;
        private bool _isCreatingNew = true;

        private DataTable _globalParameters;
        public DataTable GlobalParameters
        {
            get { return _globalParameters; }
        }

        public ObservableCollection<FileModel> RecentFiles { get; set; }

        private ObservableCollection<ConfigViewModel> _configViewModels = 
            new ObservableCollection<ConfigViewModel>();
        private ReadOnlyObservableCollection<ConfigViewModel> _readonlyConfigViewModels = null;
        public ReadOnlyObservableCollection<ConfigViewModel> ConfigViews
        {
            get
            {
                if (_readonlyConfigViewModels == null)
                    _readonlyConfigViewModels = 
                        new ReadOnlyObservableCollection<ConfigViewModel>(_configViewModels);

                return _readonlyConfigViewModels;
            }
        }

        ToolViewModel[] _tools = null;
        public IEnumerable<ToolViewModel> Tools
        {
            get
            {
                if (_tools == null)
                    _tools = new ToolViewModel[] { new LogViewModel() };
                return _tools;
            }
        }

        private LogViewModel _logView;
        public LogViewModel LogViewModel
        {
            get 
            {
                if (_logView == null)
                    _logView = new LogViewModel();
                return _logView;
            }
        }
        
        public ConfigViewModel ConfigViewModel
        {
            get { return _configViewModel; }
            set
            {
                if (_configViewModel != value)
                {
                    _configViewModel = value;
                    RaisePropertyChanged("ConfigViewModel");
                }
            }
        }

        public List<string> EncodingNames
        {
            get 
            {
                var names = Encoding.GetEncodings().Select(e => e.Name).ToList();
                names.Remove("utf-8");
                names.Insert(0, "utf-8 with signature");
                names.Insert(1, "utf-8 without signature");
                return names;
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            _configViewModel = new ConfigViewModel();
            _configViewModels.Add(_configViewModel);

            if (IsInDesignMode)
                return;

            RecentFiles = LoadRecentFiles();

            _openFileDialog = new Microsoft.Win32.OpenFileDialog
            {
                 Filter = CONFIG_FILTER
            };

            _saveFileDialog = new Microsoft.Win32.SaveFileDialog
            {
                Filter = CONFIG_FILTER,
                DefaultExt = "config",
            };

            this.ReadGlobalParameters();
        }

        private void SaveGlobalParameters()
        {
            try
            {
                if (_globalParameters != null && _globalParameters.Rows.Count > 0)
                {
                    var invalidRows = _globalParameters.AsEnumerable()
                        .Where(r => string.IsNullOrEmpty((string)r["NAME"])).ToList();
                    foreach (var invalidRow in invalidRows)
                        _globalParameters.Rows.Remove(invalidRow);
                    _globalParameters.AcceptChanges();
                    _globalParameters.WriteXml(GLOBAL_PARAMS_FILE);
                }
            }
            catch (Exception ex)
            {
                Utils.ShowError(ex.Message);
            }
        }

        private void ReadGlobalParameters()
        {
            try
            {
                _globalParameters = new DataTable("GlobalParameters");
                var colName = _globalParameters.Columns.Add("NAME", typeof(string));
                colName.DefaultValue = "";
                var colValue = _globalParameters.Columns.Add("VALUE", typeof(string)); ;
                colValue.DefaultValue = "";
                if (File.Exists(GLOBAL_PARAMS_FILE))
                {
                    _globalParameters.ReadXml(GLOBAL_PARAMS_FILE);
                }
            }
            catch (Exception ex)
            {
                Utils.ShowError(ex.Message);
            }
        }

        private void SaveAs()
        {
            var ret = _saveFileDialog.ShowDialog();
            _saveFileDialog.InitialDirectory = ConfigViewModel.GeneratorConfig.WorkDir;
            if (ret.HasValue && ret.Value)
            {
                try
                {
                    var saveAsFileName = _saveFileDialog.FileName;
                    if (File.Exists(saveAsFileName))
                        File.Delete(saveAsFileName);

                    ConfigViewModel.SaveAs(saveAsFileName, false);
                    ConfigViewModel.ConfigFileName = _saveFileDialog.FileName;
                    Utils.ShowInfo("Save '{0}' Successfully.", Path.GetFileName(saveAsFileName));
                }
                catch (Exception ex)
                {
                    Utils.ShowError(ex.Message);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns>true:save, false:don't save, null:cancel</returns>
        private bool? CheckSaved()
        {
            // check unsave.
            if (ConfigViewModel.IsDirty)
            {
                var ret = Utils.ShowConfirmYesNoCancel("Save changes to {0}?", ConfigViewModel.Name);
                if (ret == MessageBoxResult.Yes)
                {
                    SaveCommand.Execute(null);
                    return true;
                }
                else if (ret == MessageBoxResult.No)
                {
                    return false;
                }
                else
                {
                    return null;
                }
            }
            return true;
        }

        private ObservableCollection<FileModel> LoadRecentFiles()
        {
            var fileViewModels = new ObservableCollection<FileModel>();
            try
            {
                var files = File.ReadAllLines("RecentFiles");
                foreach (var f in files)
                {
                    fileViewModels.Add(new FileModel
                    {
                        Index = fileViewModels.Count + 1,
                        FullPath = f,
                        FileName = Path.GetFileName(f)
                    });
                }
            }
            catch 
            { 
               // ignore all errors.
            }
            return fileViewModels;
        }

        private void AddAndSaveRecentFiles(string fileName)
        {
            try
            {
                if (RecentFiles.FirstOrDefault(f => f.FullPath == fileName) == null)
                {
                    if (RecentFiles.Count > RECENT_FILES_LIMIT - 1)
                    {
                        RecentFiles.RemoveAt(RECENT_FILES_LIMIT - 1);
                    }

                    RecentFiles.Add(new FileModel
                    {
                        Index = RecentFiles.Count + 1,
                        FileName = Path.GetFileName(fileName),
                        FullPath = fileName
                    });
                    var contents = string.Join("\n", RecentFiles.Select(f => f.FullPath).ToArray());
                    File.WriteAllText("RecentFiles", contents);
                }
            }
            catch 
            { 
                // ignore all errors.
            }
        }

        #region Commands
        private RelayCommand _newCommand;
        public RelayCommand NewCommand
        {
            get {
                if (_newCommand == null)
                {
                    _newCommand = new RelayCommand(() => {

                        var saved = CheckSaved();
                        if (saved == null)
                            return;

                        ConfigViewModel.Load();
                        _isCreatingNew = true;
                    });
                }
                return _newCommand;
            }
        }

        private RelayCommand<string> _openCommand;
        public RelayCommand<string> OpenCommand
        {
            get
            {
                if (_openCommand == null)
                {
                    _openCommand = new RelayCommand<string>(fileName =>
                    {
                        var saved = CheckSaved();
                        if (saved == null)
                            return;
                        
                        if (string.IsNullOrEmpty(fileName))
                        {
                            var ret = _openFileDialog.ShowDialog();
                            if (ret.HasValue && ret.Value)
                            {
                                fileName = _openFileDialog.FileName;
                            }
                            else
                            {
                                return;
                            }
                        }
                        else if (fileName == "sample")
                        {
                            fileName = Path.Combine(Environment.CurrentDirectory, "Sample\\Sample.config");
                        }

                        try
                        {
                            ConfigViewModel.Load(fileName);
                            AddAndSaveRecentFiles(fileName);
                            _isCreatingNew = false;
                        }
                        catch (Exception ex)
                        {
                            Utils.ShowError(ex.Message);
                        }
                    });
                }
                return _openCommand;
            }
        }

        private RelayCommand _saveCommand;
        public RelayCommand SaveCommand
        {
            get
            {
                if (_saveCommand == null)
                {
                    _saveCommand = new RelayCommand(() =>
                    {
                        if (_isCreatingNew)
                        {
                            SaveAs();
                        }
                        else
                        {
                            try
                            {
                                ConfigViewModel.Save();
                                Utils.ShowInfo("Saved Successfully.");
                            }
                            catch (Exception ex)
                            {
                                Utils.ShowError(ex.Message);
                            }
                        }
                    });
                }
                return _saveCommand;
            }
        }

        private RelayCommand _saveAsCommand;
        public RelayCommand SaveAsCommand
        {
            get 
            {
                if (_saveAsCommand == null)
                {
                    _saveAsCommand = new RelayCommand(SaveAs);
                }
                return _saveAsCommand;
            }
        }


        private RelayCommand<CancelEventArgs> _closingCommand;
        public RelayCommand<CancelEventArgs> ClosingCommand
        {
            get
            {
                if (_closingCommand == null)
                {
                    _closingCommand = new RelayCommand<CancelEventArgs>((e) =>
                    {
                        var saved = CheckSaved();
                        if (saved == null)
                            e.Cancel = true;
                    });
                }
                return _closingCommand;
            }
        }

        private RelayCommand _exitCommand;
        public RelayCommand ExitCommand
        {
            get
            {
                if (_exitCommand == null)
                {
                    _exitCommand = new RelayCommand(() =>
                    {
                        Application.Current.MainWindow.Close();
                    });
                }
                return _exitCommand;
            }
        }

        private RelayCommand _executeCommand;
        public RelayCommand ExecuteCommand
        {
            get
            {
                if (_executeCommand == null)
                {
                    _executeCommand = new RelayCommand(() =>
                    {
                        LogViewModel.ExecutingLog = "";
                        this.SaveGlobalParameters();
                        ConfigViewModel.GlobalParameters = GlobalParameters;
                        ConfigViewModel.RunCommand.Execute(null);   
                    });
                }
                return _executeCommand;
            }
        }

        private RelayCommand _cancelCommand;
        public RelayCommand CancelCommand
        {
            get
            {
                if (_cancelCommand == null)
                {
                    _cancelCommand = new RelayCommand(() =>
                    {
                        ConfigViewModel.StopCommand.Execute(null);
                    });
                }
                return _cancelCommand;
            }
        }

        private RelayCommand<KeyEventArgs> _keyDownCommand;
        public RelayCommand<KeyEventArgs> KeyDownCommand
        {
            get
            {
                if (_keyDownCommand == null)
                {
                    _keyDownCommand = new RelayCommand<KeyEventArgs>((e) =>
                    {
                        if (e.Key == Key.F5)
                            ExecuteCommand.Execute(null);
                        //if (e.Key == Key.Escape)
                        //    CancelCommand.Execute(null);
                    });
                }
                return _keyDownCommand;
            }
        }

        private RelayCommand _aboutCommand;
        public RelayCommand AboutCommand
        {
            get
            {
                if (_aboutCommand == null)
                {
                    _aboutCommand = new RelayCommand(() =>
                        {
                            Process.Start("http://razorsourcegenerator.codeplex.com/");
                        });
                }
                return _aboutCommand;
            }
        }

        private RelayCommand _addGlobalParamCommand;
        public RelayCommand AddGlobalParamCommand
        {
            get
            {
                if (_addGlobalParamCommand == null)
                {
                    _addGlobalParamCommand = new RelayCommand(() =>
                    {
                        var row = this._globalParameters.NewRow();
                        this._globalParameters.Rows.Add(row);
                    });
                }
                return _addGlobalParamCommand;
            }
        }

        private RelayCommand<DataRowView> _removeGlobalParamCommand;
        public RelayCommand<DataRowView> RemoveGlobalParamCommand
        {
            get
            {
                if (_removeGlobalParamCommand == null)
                {
                    _removeGlobalParamCommand = new RelayCommand<DataRowView>(row =>
                    {
                        if (row != null)
                        {
                            var realRow = row.Row;
                            _globalParameters.Rows.Remove(realRow);
                        }
                    });
                }
                return _removeGlobalParamCommand;
            }
        }

        #endregion
    }
}